package commons.params.param_component;

import commons.params.A_ParamsManager;
import commons.params.ihm.MyTable;
import commons.utils.Properties_INI;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Properties;
import javax.swing.JComponent;
import commons.utils.Utils;
import java.util.ArrayList;


public abstract class AbstractParamComponent_table extends AbstractParamComponent implements ActionListener {

	protected int NB_COL;

	protected MyTable table;

	protected String[] columnNames;
	protected String[] columnCodes; // nom simplifié pour l'écriture dans fichier .prop
	protected Class[] columnTypes;
	protected ArrayList<String>[] possibleValues; //ième élément du tableau = vecteur de valeur pour la ième colonne
	protected ArrayList<Object[]> data; // ième élement du vecteur = tableau de valeurs pour la ième ligne
	
	protected Integer[] columnSize;
	public void setColumnSize(Integer[] columnSize) {
		this.columnSize = columnSize;
	}

	// Table des message d'erreur, renseigné dans la méthode makeStandardControls()
	protected ArrayList<String[]> msgError;
	protected int nbError;

	public AbstractParamComponent_table(String name, A_ParamsManager paramsManager, JComponent component, boolean bCanBeNull, String[] columnNames, String[] columnCodes, Class[] columnTypes) {
		this(name, paramsManager, component, bCanBeNull, columnNames, columnCodes, columnTypes, null);
	}

	public AbstractParamComponent_table(String name, A_ParamsManager paramsManager, JComponent component, boolean bCanBeNull, String[] columnNames, String[] columnCodes, Class[] columnTypes, I_Activator activator) {
		super(name, paramsManager, component, bCanBeNull, activator, true);

		assert component instanceof MyTable;
		assert (columnNames.length == columnCodes.length && columnNames.length == columnTypes.length);
		NB_COL = columnNames.length;
		this.columnNames = columnNames;
		this.columnCodes = columnCodes;
		this.columnTypes = columnTypes;

		this.table = (MyTable) component;
		this.table.setParamComponentListener(this);
		this.table.setActivator(activator);



		this.possibleValues = new ArrayList[NB_COL];
		data = new ArrayList<Object[]>();

		table.initializeTable(columnNames, columnTypes, data);
	}

	// Prévient le ParamManager à la moindre modif utilisateur dans la table
	// Appelée par MyTable
	@Override
	public void actionPerformed(ActionEvent e) {
		//System.out.println("TABLE MODIF USER");
		paramsManager.traiteOneModifUtilisateur();
	}

	// Renvoit la liste des noms entrés par l'utilisateur dans la 1ere colonne
	public ArrayList<String> getListValue(int numCol) {
		ArrayList<String> vRes = new ArrayList<String>();
		for (Object[] rowData : data)
			if (rowData[numCol]!=null && ((String)rowData[numCol]).length()>0)
				vRes.add(rowData[numCol].toString());
		return vRes;
	}

	//**************************************************************************
	//**************************************************************************


	@Override
	public Object getValue() {
		return data;
	}

	@Override
	public void setValue(Object val) {
		if (val==null)
			this.data = new ArrayList<Object[]>();
		else {
			assert (val instanceof ArrayList);
			if ( ((ArrayList)val).size()>0 )
				assert (((ArrayList)val).get(0) instanceof Object[]);
			this.data = (ArrayList)val;
		}
		bIsNull = data.size()==0;
	}

	@Override
	public void setPossibleValues(ArrayList<String>[] possibleValues) {
		assert (possibleValues.length == columnNames.length);
		this.possibleValues = possibleValues;
		table.setPossibleValues(possibleValues);
	}

	@Override
	public void writeToComponent() {
		table.initializeTable(columnNames, columnTypes, data);
		
		// MAJ de la taille des colonnes

		if (columnSize != null) {
			for (int numCol=0; numCol<columnSize.length; numCol++) {
				if (columnSize[numCol]>0)
					table.setColumnSize(numCol, columnSize[numCol]);
			}
		}
	}

	@Override
	public void readFromComponent() {
		data = table.getData();
		bIsNull = data.size()==0;
	}

	@Override
	public void storeInProperties(Properties_INI props) {
		// On stocke d'abord le nombre d'éléments
		props.setProperty(name + ".nbValeur", ""+data.size());
		// Puis les éléments eux-même
		int numRow = 0;
		String strVal="";
		for (Object[] rowData : data) {
			for (int numCol=0 ; numCol<NB_COL ; numCol++) {
				if (rowData[numCol] == null)
						strVal = "";
				else if (columnTypes[numCol] == String.class)
					strVal = rowData[numCol].toString();

				else if (columnTypes[numCol] == Color.class)
					// On stocke l'entier codant la couleur (format RGB)
					strVal = "" + ((Color)rowData[numCol]).getRGB();

				else
					assert false;

				props.setProperty(name + "." + columnCodes[numCol] + "_" + numRow, strVal);
			}
				
			numRow++;
		}
	}

	@Override
	public boolean loadFromProperties(Properties_INI props) {
		data.clear();
		bIsNull = true;

		// Lecture du nombre de ligne du tableau
		if (!isThereProperty_int_pos(props, name + ".nbValeur"))
			return false;
		int nbCat = readProperty_int(props, name + ".nbValeur");


		// Puis les éléments eux-même
		for (int numRow=0; numRow<nbCat ; numRow++) {
			Object[] rowData = new Object[NB_COL];
			for (int numCol=0 ; numCol<NB_COL ; numCol++) {

				String strProp;
				if (!isThereProperty(props, name + "." + columnCodes[numCol] + "_" + numRow))
					//return false;
					strProp = "?";
				else
					strProp = readProperty(props, name + "." + columnCodes[numCol] + "_" + numRow);

				if (columnTypes[numCol] == String.class)
					rowData[numCol] = strProp;
				else if (columnTypes[numCol] == Color.class) {
					if (Utils.isInteger(strProp))
						rowData[numCol] = new Color(Integer.parseInt(strProp));
					else
						rowData[numCol] = new Color(0);
				}
				else
					assert false;
			}
			data.add (rowData);
		}

		bIsNull = data.size()==0;
		return true;
	}



	//**************************************************************************
	// GESTION DES CONTROLES ET DES ERREURS
	//**************************************************************************

	// Traitement des validation invalidation standard de l'ensemble de la table
	// Traitement par défaut + ajout d'un petit message sous la table
	//  en effet si la table est vide, les toolips du traitement standard d'invalidation
	//  ne s'affichent pas
	@Override
	public void invalidateComponent(String errMsg, boolean bDrawInvalidBorder) {
		super.invalidateComponent(errMsg, bDrawInvalidBorder);
		table.setSingleErrMsg(errMsg);
	}
	@Override
	public void validateComponent() {
		super.validateComponent();
		table.setSingleErrMsg("");
	}

	protected void beginControls() {

		table.setErrMsg(null, 0);

		// Initialisation d'un tableau de message d'erreur, vide par défaut
		msgError = new ArrayList<String[]>();
		for (int i=0; i<data.size() ; i++)
			msgError.add(new String[NB_COL]);

		nbError = 0;
	}

	protected void addError (int row, int col, String msg) {
		msgError.get(row)[col] = msg;
		nbError++;
	}

	// ********* Qques tests standard prédéfinis *******************************

	protected boolean basicCheck (int row, int col) {
		String value = (String) data.get(row)[col];
		// Check valeur obligatoire
		if (value==null || value.trim().length()==0) {
			addError(row, col, "Paramètre obligatoire");
			return false;
		}
		else {
			// la valeur doit faire partie des valeurs possibles
			if (possibleValues[col]!= null && !possibleValues[col].contains(value)) {
				addError(row, col, "Ce paramètre doit faire partie des valeurs possibles");
				return false;
			}
		}
		return true;
	}

	protected boolean basicCheck_canBeNull (int row, int col) {
		String value = (String) data.get(row)[col];
		
		if (value==null || value.trim().length()==0) {
			return true;
		}
		else {
			// la valeur doit faire partie des valeurs possibles
			if (possibleValues[col]!= null && !possibleValues[col].contains(value)) {
				addError(row, col, "Ce paramètre doit faire partie des valeurs possibles");
				return false;
			}
		}
		return true;
	}

	protected boolean basicCheck_Float (int row, int col) {
		if (!basicCheck(row, col))
			return false;

		String value = (String) data.get(row)[col];
		if (!Utils.isFloat(value)) {
			addError(row, col, "Ce paramètre doit être un nombre réel");
			return false;
		}
		
		return true;
	}
	protected boolean basicCheck_FloatPos (int row, int col) {
		if (!basicCheck(row, col))
			return false;

		String value = (String) data.get(row)[col];
		if (!Utils.isFloat_Pos(value)) {
			addError(row, col, "Ce paramètre doit être un nombre réel positif ou nul");
			return false;
		}

		return true;
	}
	protected boolean basicCheck_FloatStrictPos (int row, int col) {
		if (!basicCheck(row, col))
			return false;

		String value = (String) data.get(row)[col];
		if (!Utils.isFloat_strictPos(value)) {
			addError(row, col, "Ce paramètre doit être un nombre réel strictement positif");
			return false;
		}

		return true;
	}
	protected boolean basicCheck_IntPos (int row, int col) {
		if (!basicCheck(row, col))
			return false;

		String value = (String) data.get(row)[col];
		if (!Utils.isInteger_Pos(value)) {
			addError(row, col, "Ce paramètre doit être un entier positif ou nul");
			return false;
		}

		return true;
	}	protected boolean basicCheck_IntStrictPos (int row, int col) {
		if (!basicCheck(row, col))
			return false;

		String value = (String) data.get(row)[col];
		if (!Utils.isInteger_strictPos(value)) {
			addError(row, col, "Ce paramètre doit être un entier strictement positif");
			return false;
		}
		
		return true;
	}
	protected boolean  basicCheck_NoDoublon (int row, int col) {
		if (!basicCheck(row, col))
			return false;

		ArrayList<String> vValues = getListValue(col);
		String value = (String) data.get(row)[col];
		// Pas de doublon dans les noms de catégories
		int index = vValues.indexOf(value);
		if (Utils.indexOf(vValues, value, index+1) != -1) {
			addError(row, col, "Valeur en doublon");
			return false;
		}

		return true;
	}

	protected void finaliseControls() {

		if (nbError>0)
			invalidateComponent("Erreurs dans les valeurs de la table", false);
		// pas besoin de valider si pas d'erreur, car tous les component sont validés par
		//  défaut par le ParamManager avant le controle des erreurs

		table.setErrMsg(msgError, nbError);
	}
}
